#define	_ARH_KONTEKST_C_
#include "kontekst.h"
#include "spremnik.h"
#include <jezgra/ispis.h>
#include <jezgra/procesi.h>
#include <jezgra/spremnik.h>
#include <jezgra/stranicenje.h>

//opis konteksta dretve u koju se vraca: gdje je kontekst
volatile uint16 arh_opisnik_konteksta = 0;	//opisnik segmenta gdje je stog
volatile uint32 arh_kontekst_aktivne = 0;	//adresa vrha stoga s kontekstom
volatile uint32 arh_opisnik_stranica_cr0 = 0x01;
volatile uint32 arh_opisnik_stranica_cr3 = 0;

//prije povratka u dretvu
void arh_pripremi_kontekst (void *_kontekst)
{
	arh_kontekst_t *kontekst = _kontekst;
	
	arh_opisnik_konteksta = kontekst->ss;
	arh_kontekst_aktivne = kontekst->esp;
	arh_opisnik_stranica_cr0 = kontekst->spremnik->cr0;
	arh_opisnik_stranica_cr3 = kontekst->spremnik->cr3;

	/*j_ispisi ( "cr0=%x, cr3=%x, ss=%x, esp=%x\n", arh_opisnik_stranica_cr0,
		arh_opisnik_stranica_cr3, (uint) arh_opisnik_konteksta,
		arh_kontekst_aktivne );
	*/
}

//pri prekidu, prije obrade
void *arh_sacuvaj_kontekst (void *_kontekst )
{
	arh_kontekst_t *kontekst = _kontekst;

	kontekst->esp = arh_kontekst_aktivne;
	//j_ispisi ("esp=%x\n", kontekst->esp);
	//kontekst->ss = arh_opisnik_konteksta;
	//kontekst->cr0 = arh_opisnik_stranica_cr0;
	//kontekst->cr3 = arh_opisnik_stranica_cr3;

	if ( kontekst->spremnik->razina == RAZINA_KORISNIKA )
	{
		//kontekst je u mjestu za kontekst
		return j_puj ( -1, (void *) kontekst->kontekst.esp );
	}
	else {	//kontekst je na stogu dretve
		return j_puj ( -1, ( (void *) kontekst->esp ) + 
			( sizeof (kontekst_dretve_t) - 2 * sizeof(uint32) ) );
	}
}

// stog dretve mora biti spreman za povratak iz prekida
void arh_izgradi_pocetni_stog ( void *_spremnik,
			        void *a_kontekst, void *l_kontekst,
				void *a_stog, void *l_stog,
				void *pokreni_dretvu,
				void *poc_funkc, void *param )
{
	arh_spremnik_t *spremnik = _spremnik;
	arh_kontekst_t *kontekst = a_kontekst;
	void *astog = a_stog;
	kontekst_dretve_t *pstog;

	//stavi parametre na dretvin stog
	STAVI_NA_STOG ( astog, param );
	STAVI_NA_STOG ( astog, poc_funkc );
	STAVI_NA_STOG ( astog, 0 );

	if ( spremnik->razina == RAZINA_KORISNIKA ) {
		pstog = &kontekst->kontekst; //kontekst se sprema u opisnik dr.

		pstog->eflags = POC_REG_STANJA;	// registar stanja

		pstog->eip = (uint32) pokreni_dretvu;	// programsko br.

		//dretvin stog (koji koristi van jezgre)
		pstog->ss = spremnik->op_podataka;
		//pstog->esp = (uint32) arh_jup ( spremnik, astog );
		pstog->esp = ( (uint32) l_stog ) -
			( ( (uint32) a_stog ) - (uint32) astog );

		//opisnik stoga za obnovu (pohranu) konteksta - u jezgri
		//if ( !spremnik->stranicenje )
			kontekst->ss =
				GDT_OPISNIK ( SEGJ_PODACI, GDT, RAZINA_JEZGRE );
		//else
			//kontekst->ss = spremnik->op_podataka;
		
		if ( !spremnik->stranicenje )
			kontekst->esp = (uint32) a_kontekst;
		else
			kontekst->esp = (uint32) l_kontekst;
	}
	else {	//jezgrina dretva
		//"malo drukcije" - koristi se samo jedan stog za sve!
		pstog =  astog - 
			( sizeof (kontekst_dretve_t) - 2 * sizeof (uint32) );

		pstog->eflags = POC_REG_STANJA_JD; // registar stanja - u jezgri

		if ( spremnik->odmak == 0 && !spremnik->stranicenje ) {
			//"umetnuta" funkcija za stvaranje dretvi je u relat.
			//adresnom prostoru, a ova dretva je u apsolutnom
			extern char programi_pocetak;
			pstog->eip = ( (uint32) pokreni_dretvu ) +
				     ( (uint32) &programi_pocetak );
		}
		else {
			pstog->eip = (uint32) pokreni_dretvu; //programsko br.
		}

		//ne dirati pstog->ss i pstog->esp - ne koriste se, tj. na tim
		//se adresama na stogu nesto nalazi

		//opisnik stoga za obnovu/pohranu konteksta - dretvin stog!
		kontekst->ss = spremnik->op_podataka;

		//kontekst->esp = (uint32) arh_jup ( spremnik, pstog );
		kontekst->esp = ( (uint32) l_stog ) -
				( ( (uint32) a_stog ) - (uint32) pstog );
	}

	pstog->cs = spremnik->op_koda;
	pstog->ds = spremnik->op_podataka;
	pstog->es = spremnik->op_podataka;
	pstog->fs = spremnik->op_podataka;
	pstog->gs = spremnik->op_podataka;

	pstog->kod_greske = 0;	// zamjenski kod greske

	// sadrzaj opcih registara je nebitan (pstog->registri[])

	//arh_ispisi_kontekst(pstog);
	//j_ispisi ("ss=%x, esp=%x\n", (uint) kontekst->ss, kontekst->esp );
}

void *arh_dodaj_obradu ( void *_spremnik, void *_kontekst, void *obrada,
			 uint32 vp_na_stogu, ... )
//"vp_na_stogu" = velicina parametara (...) na stogu u 'uint32' jedinicama
//umetnuta funkcija mora izgledati: um_fun(void *stari_vrh_stoga, ...)
//ali ne koristiti 'stari_vrh_stoga'! (ako se taj stog promijeni ...!)
{
	j_spremnik_t *jspremnik = _spremnik;
	arh_spremnik_t *spremnik = &jspremnik->segmentacija;
	arh_kontekst_t *kontekst = _kontekst;
	kontekst_dretve_t *kontekst_dr, *kopija;
	uint32 *param = &vp_na_stogu;
	void *stog_prije, *stog_poslije;

	uint vel_param = 2 + vp_na_stogu; //u "uint32" jedinicama
	uint vel_kont;

	uint mjesto_za_kontekst, mjesto_za_parametre;

	//kontekst_dr = kontekst dretve (u apsolutnim adresama)
	//stog_prije = vrh stoga dretve (relativne adrese)

	if ( spremnik->razina == RAZINA_KORISNIKA )
	{
		kontekst_dr = &kontekst->kontekst;

		stog_prije = (void *) kontekst_dr->esp;

		vel_kont = sizeof(kontekst_dretve_t) / sizeof (uint32);

		mjesto_za_kontekst =	vel_param;
		mjesto_za_parametre =	0;
	}
	else {	//razina jezgre - kontekst je na stogu koji je u log. adr.!
		stog_prije = kontekst_dr = (void *) kontekst->esp;

		vel_kont = sizeof(kontekst_dretve_t) / sizeof (uint32) - 2;

		mjesto_za_kontekst =	0;
		mjesto_za_parametre =	vel_kont;
	}

	stog_poslije = stog_prije - ( vel_kont + vel_param ) * sizeof(uint32);
	uint vel_umetka = ( vel_kont + vel_param ) * sizeof(uint32);


	//umetni na stog dretve parametre (pripremi za kopiranje)

	uint32 umetak[ vel_kont + vel_param ];

	umetak[mjesto_za_parametre++] = 0;	//povratna adresa
	umetak[mjesto_za_parametre++] = (uint32) stog_prije; //stog_prije
	memcpy ( &umetak[mjesto_za_parametre],  param + 1,
		 vp_na_stogu * sizeof(uint32) );

	//kopiraj kontekst na stog (pripremi za kopiranje)
	kopija = (void *) &umetak[mjesto_za_kontekst];

	if ( spremnik->razina == RAZINA_KORISNIKA )
	{
		//razina dretve - kontekst se nalazi na "kontekst_dr" (aps.adr.)
		memcpy ( kopija, kontekst_dr, vel_kont * sizeof(uint32) );

		//promijeni kontekst na mjestu konteksta
		kontekst_dr->esp = (uint32) stog_poslije;
		kontekst_dr->eip = (uint32) obrada;
	}
	else {
		//razina jezgre - kontekst se nalazi na stogu dretve, na adresi
		//"kontekst_dr" (logicka adresa!)
		if ( jspremnik->stranicenje )
		{
			j_kopiraj_u_ili_iz_procesa ( kopija, kontekst_dr,
						     vel_kont * sizeof(uint32),
						     jspremnik,
						     KOPIRAJ | U_JEZGRU );
		}
		else {
			memcpy ( kopija, arh_puj ( spremnik, kontekst_dr ),
				 vel_kont * sizeof(uint32) );
		}

		//oznaci promjene na kontekstu
		kopija->eip = (uint32) obrada;
		kontekst->esp = (uint32) stog_poslije;
	}

	//umetni na stog dretve pripremljeni 'umetak'
	if ( jspremnik->stranicenje )
	{
		j_kopiraj_u_ili_iz_procesa ( &umetak[0], stog_poslije,
					     vel_umetka, jspremnik,
					     KOPIRAJ | U_PROCES | ZA_PROCES );
	}
	else {
		memcpy ( arh_puj ( spremnik, stog_poslije ), &umetak[0], 
			 vel_umetka);
	}

	return stog_prije;
}

//makni obradu sa stoga
//vrh_stoga => u logickim adresama!
void arh_makni_obradu (void *_spremnik, void *_kontekst, void *vrh_stoga)
{
	j_spremnik_t *jspremnik = _spremnik;
	arh_spremnik_t *spremnik = &jspremnik->segmentacija;

	arh_kontekst_t *kontekst = _kontekst;
	kontekst_dretve_t *kontekst_dr = &kontekst->kontekst, *kopija;

	if (kontekst->spremnik->razina == RAZINA_KORISNIKA) {
		kopija = vrh_stoga;
		kopija--; //stari kontekst je na vrhu stoga (iznad 'vrh_stoga')
	
		if ( jspremnik->stranicenje )
			j_kopiraj_u_ili_iz_procesa ( kontekst_dr, kopija,
						     sizeof(kontekst_dretve_t),
						     jspremnik,
						     KOPIRAJ | U_JEZGRU );
		else
			memcpy ( kontekst_dr, arh_puj ( spremnik, kopija ),
				 sizeof(kontekst_dretve_t));
	}
	else {
		kontekst->esp = (uint32) vrh_stoga;
	}
}

void arh_ispisi_kontekst ( void *_kontekst )
{
	kontekst_dretve_t *k = _kontekst;

	j_ispisi ( ">>>\nsegm:\t%x %x %x %x\n", (uint32) k->gs, (uint32) k->fs,
		   (uint32) k->es, (uint32) k->ds );

	j_ispisi ( "kodgr:\t%d\n", k->kod_greske );

	j_ispisi ( "eip:\t%x\t", k->eip );

	j_ispisi ( "cs:\t%x\t", k->cs );

	j_ispisi ( "eflags:\t%x\n", k->eflags );

	j_ispisi ( "esp:\t%x\t", k->esp );

	j_ispisi ( "ss:\t%x\n<<<\n", k->ss );
}

/*

http://www.angelcode.com/dev/callconv/callconv.html
http://en.wikipedia.org/wiki/X86_calling_conventions

*/